package context

import (
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"time"
)

type (
	param struct {
		Name  string
		Value string
		//Method string
	}
	Params []param
)

// 获取参数值
func (p *Params) Get(name string) (string, bool) {
	name = strings.ToLower(name)
	for _, v := range *p {
		if strings.EqualFold(v.Name, name) {
			return v.Value, true
		}
	}

	return "", false
}

// 将参数转为map对象
func (p *Params) ParseMap() map[string]interface{} {
	m := make(map[string]interface{}, len(*p))

	for _, v := range *p {
		m[v.Name] = v.Value
	}

	return m
}

// 将参数填充到结构体中
func (p *Params) ParseStruct(obj interface{}) error {
	objT := reflect.TypeOf(obj)
	objV := reflect.ValueOf(obj)
	if !isStructPtr(objT) {
		return fmt.Errorf("%v must be a struct pointer", obj)
	}
	objT = objT.Elem()
	objV = objV.Elem()

	return p.parseStruct(objT, objV)
}

func (p *Params) parseStruct(objT reflect.Type, objV reflect.Value) error {
	for i := 0; i < objT.NumField(); i++ {
		fieldV := objV.Field(i)
		if !fieldV.CanSet() {
			continue
		}

		fieldT := objT.Field(i)
		if fieldT.Anonymous && fieldT.Type.Kind() == reflect.Struct {
			err := p.parseStruct(fieldT.Type, fieldV)
			if err != nil {
				return err
			}
			continue
		}

		tags := strings.Split(fieldT.Tag.Get("json"), ",")
		var tag string
		if len(tags) == 0 || len(tags[0]) == 0 {
			tag = fieldT.Name
		} else if tags[0] == "-" {
			continue
		} else {
			tag = tags[0]
		}

		value, _ := p.Get(tag)
		if len(value) == 0 {
			continue
		}

		switch fieldT.Type.Kind() {
		case reflect.Bool:
			if strings.ToLower(value) == "on" || strings.ToLower(value) == "1" || strings.ToLower(value) == "yes" {
				fieldV.SetBool(true)
				continue
			}
			if strings.ToLower(value) == "off" || strings.ToLower(value) == "0" || strings.ToLower(value) == "no" {
				fieldV.SetBool(false)
				continue
			}
			b, err := strconv.ParseBool(value)
			if err != nil {
				//重新包装错误
				err = errors.New(fmt.Sprintf("%v参数的值必须是布尔值", tag))
				return err
			}
			fieldV.SetBool(b)
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			x, err := strconv.ParseInt(value, 10, 64)
			if err != nil {
				//重新包装错误
				err = errors.New(fmt.Sprintf("%v参数的值必须是整型", tag))
				return err
			}
			fieldV.SetInt(x)
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			x, err := strconv.ParseUint(value, 10, 64)
			if err != nil {
				//重新包装错误
				err = errors.New(fmt.Sprintf("%v参数的值必须是无符号整型", tag))
				return err
			}
			fieldV.SetUint(x)
		case reflect.Float32, reflect.Float64:
			x, err := strconv.ParseFloat(value, 64)
			if err != nil {
				//重新包装错误
				err = errors.New(fmt.Sprintf("%v参数的值必须是浮点数字", tag))
				return err
			}
			fieldV.SetFloat(x)
		case reflect.Interface:
			fieldV.Set(reflect.ValueOf(value))
		case reflect.String:
			fieldV.SetString(value)
		case reflect.Struct:
			switch fieldT.Type.String() {
			case "time.Time":
				format := time.RFC3339
				if len(tags) > 1 {
					format = tags[1]
				}
				t, err := time.ParseInLocation(format, value, time.Local)
				if err != nil {
					//重新包装错误
					err = errors.New(fmt.Sprintf("%v参数的值必须符合time.Local", tag))
					return err
				}
				fieldV.Set(reflect.ValueOf(t))
			}
			//		case reflect.Slice:
			//			if fieldT.Type == sliceOfInts {
			//				formVals := form[tag]
			//				fieldV.Set(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(int(1))), len(formVals), len(formVals)))
			//				for i := 0; i < len(formVals); i++ {
			//					val, err := strconv.Atoi(formVals[i])
			//					if err != nil {
			//						return err
			//					}
			//					fieldV.Index(i).SetInt(int64(val))
			//				}
			//			} else if fieldT.Type == sliceOfStrings {
			//				formVals := form[tag]
			//				fieldV.Set(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf("")), len(formVals), len(formVals)))
			//				for i := 0; i < len(formVals); i++ {
			//					fieldV.Index(i).SetString(formVals[i])
			//				}
			//			}
		}
	}
	return nil
}

var sliceOfInts = reflect.TypeOf([]int(nil))
var sliceOfStrings = reflect.TypeOf([]string(nil))

func isStructPtr(t reflect.Type) bool {
	return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
}
